package cn.mujiankeji.mbrowser.界面.标签页.网页

import android.os.Build
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.Color
import cn.mujiankeji.mbrowser.app.应用接口
import cn.mujiankeji.mbrowser.app.应用操作.应用网页操作
import cn.mujiankeji.mbrowser.app.应用路由
import cn.mujiankeji.mbrowser.功能.广告拦截.广告拦截操作
import cn.mujiankeji.mbrowser.功能.浏览框.模型.浏览框功能枚举
import cn.mujiankeji.mbrowser.功能.浏览框.浏览框加载状态
import cn.mujiankeji.mbrowser.功能.浏览框.浏览框状态
import cn.mujiankeji.mbrowser.功能.浏览框.模型.网络文件信息
import cn.mujiankeji.mbrowser.工具.mlog
import cn.mujiankeji.mbrowser.工具.操作.用户脚本操作
import cn.mujiankeji.mbrowser.工具.网络.网络操作
import cn.mujiankeji.mbrowser.数据.数据库.AppDatabase
import cn.mujiankeji.mbrowser.数据.数据库.DBHistory
import cn.mujiankeji.mbrowser.数据.数据库.DBWebsiteConfig
import cn.mujiankeji.mbrowser.数据.配置项.应用配置
import cn.mujiankeji.mbrowser.界面.小窗页.文本编辑窗口状态
import cn.mujiankeji.mbrowser.界面.小窗页.管理.网页日记窗口状态
import cn.mujiankeji.mbrowser.界面.小窗页.请求.添加书签窗口状态
import cn.mujiankeji.mbrowser.界面.标签页.源码.源码标签页状态
import cn.mujiankeji.mbrowser.界面.标签页.阅读页面.阅读页面状态
import cn.mujiankeji.mbrowser.界面.框架.主视图.标签页状态
import com.blankj.utilcode.util.ClipboardUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import org.koin.core.context.GlobalContext
import kotlinx.datetime.Clock
import okio.Path.Companion.toPath


/**
 * 网页标签状态
 * 
 * 采用状态委托模式，减少重复状态的创建。标签页状态主要从浏览框状态中获取，
 * 而不是创建新的MutableStateFlow实例。
 */
class 网页标签状态(
    加载地址 : String,
    来源地址 : String = "",
    是否为搜索进入 : Boolean = false,
    自动翻译 : Boolean = false) : 标签页状态() {

    // 创建浏览框状态实例，包含所有核心状态
    val 浏览框状态 =
        浏览框状态(uuid = 标识,
            页面配置 = 应用网页操作.取基础配置(加载地址,来源地址,是否为搜索进入,自动翻译),
            响应状态更新 = {状态->
                加载状态变化(状态)
            },
            响应加载新的页面 = { url->加载新地址(url)},
            响应可否加载文件 = { 地址 : String, 协议头 : Map<String,String>?->
                请求加载资源(地址,协议头)
            },
            响应获取加载日记 = { 日记.value },
            响应下载 = {url: String, userAgent: String?, contentDisposition: String?, mimetype: String?, contentLength: Long->
                应用网页操作.请求下载(url,userAgent, contentDisposition, mimetype, contentLength)
            },
            响应边缘颜色变化 = {顶部 : Int?,底部 : Int? ->
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    try {
                        顶部?.let {
                            头部颜色 = Color(it)
                        }
                    }catch (e : Exception){
                        e.printStackTrace()
                        头部颜色 = null
                    }
                }

            },
            响应输入框 = {信息 : String,默认值 : String,回调 : (String?)->Unit ->
                文本编辑窗口状态(默认值,{信息}){
                    回调(it)
                }.显示()
            },
            响应确认框 = {信息 : String,回调 : (Boolean)->Unit->
                应用接口.信息框(信息){
                    回调(it)
                }
            },
            响应安装扩展或脚本 = {代码或地址 : String ->
                应用接口.安装扩展(代码或地址)
            },
            响应销毁页面 = {
                super.界面销毁()
            },
            响应显示菜单 = {
                显示长按菜单(it)
            }
        )

    // 使用composable 的 rememberCoroutineScope
    var coroutine : CoroutineScope ?= null

    // 状态委托：不再创建新的MutableStateFlow，而是直接委托给浏览框状态
    // 当上层需要读取标题时，实际读取的是浏览框状态中的标题
    override val 标题: MutableStateFlow<String?> get() = 浏览框状态.标题 as MutableStateFlow<String?>
    override val 地址: MutableStateFlow<String?> get() = 浏览框状态.地址 as MutableStateFlow<String?>

    override val 加载进度: MutableStateFlow<Int> get() = 浏览框状态.加载进度
    override val 可否后退: MutableStateFlow<Boolean> get() = 浏览框状态.可否后退
    override val 可否前进: MutableStateFlow<Boolean> get() = 浏览框状态.可否前进

    var 头部颜色 by mutableStateOf<Color?>(null)

    // -- 资源记录及广告拦截 --
    private val 日记 = MutableStateFlow<MutableList<网络文件信息>>(ArrayList())
    suspend fun 添加资源记录(item : 网络文件信息){
        日记.value.add(item)
    }
    fun 请求加载资源(url : String,协议头 : Map<String,String>?) : Boolean{
        val item = 网络文件信息(url)
        try {
            // 这里应当做拦截的
            item.广告过滤规则 = 广告拦截操作.匹配(url,地址.value?:"")
            return item.广告过滤规则 == null
        }
        finally {
            coroutine?.launch {
                添加资源记录(item)
            }
        }
    }

    fun 重载(配置 : DBWebsiteConfig){
        浏览框状态.重载(应用网页操作.更新配置(配置,浏览框状态.页面配置))
    }

    fun 加载新地址(url : String) : Boolean{
        mlog("load newurl",url)
        // 判定为脚本
        if (url.endsWith(".user.js")){
            coroutine?.launch {
                // 显示全局加载对话框
                应用接口.显示加载中界面(true, "正在下载用户脚本...")
                
                var code = 网络操作.获取文本(url,
                    headers = HashMap<String, String>().apply {
                        put("Referer",浏览框状态.地址.value)
                    })
                
                // 隐藏加载对话框
                应用接口.显示加载中界面(false)
                
                if (code != null){
                    val item = 用户脚本操作.解析(code)
                    if (item.名称.isNotEmpty()){
                        // 使用用户脚本安装窗口
                        val dao = GlobalContext.get().get<AppDatabase>().用户脚本()
                        用户脚本操作.请求安装脚本(code, dao) { 成功, _ ->
                            // 不需要在这里显示任何消息，因为用户脚本安装窗口已经提供了足够的反馈
                        }
                    } else {
                        应用接口.输出提示("无效的用户脚本")
                    }
                } else {
                    应用接口.输出提示("下载用户脚本失败")
                }
            }
            return true
        }
        return if (日记.value.size >= 2){
            // 广告跳转应当拦截
            if (请求加载资源(url,null)){
                应用路由.加载(url,浏览框状态.地址.value)
            }
            true
        }else{
            false
        }
    }

    fun 加载状态变化(状态 : 浏览框加载状态){
        mlog("state",状态)

        // 优先脚本
        coroutine?.launch {
            注入用户脚本(状态)
        }

        when(状态){
            浏览框加载状态.初始化 -> {
                加载进度.value = 0
                日记.value.clear()
            }
            浏览框加载状态.加载开始 ->{
                if (加载进度.value < 30) 加载进度.value = 30
            }

            浏览框加载状态.静态元素加载完毕 -> {
                if (加载进度.value < 80) 加载进度.value = 80
            }
            浏览框加载状态.加载完毕 -> {
                添加历史记录()
                加载进度.value = 100
            }
        }
    }

    private fun 注入用户脚本(状态: 浏览框加载状态) {
        // 如果未启用用户脚本，直接返回
        if (!浏览框状态.页面配置.启用用户脚本) return
        
        // 获取当前URL
        val 当前URL = 浏览框状态.地址.value ?: return
        
        // 将加载状态映射到运行时机
        val 运行时机 = when (状态) {
            浏览框加载状态.加载开始 -> "document-start"
            浏览框加载状态.静态元素加载完毕 -> "document-end"
            浏览框加载状态.加载完毕 -> "document-idle"
            else -> return // 其他状态不注入脚本
        }
        
        coroutine?.launch {
            try {
                // 使用全局缓存获取匹配的脚本
                val 匹配的脚本 = 用户脚本操作.匹配URL获取脚本(当前URL, 运行时机)
                
                if (匹配的脚本.isEmpty()) {
                    return@launch
                }
                
                mlog("匹配到 ${匹配的脚本.size} 个用户脚本，运行时机: $运行时机")
                
                // 如果是document-end阶段，同时注入document-start的脚本
                // 这是为了解决WebView对document-start支持不准确的问题
                if (运行时机 == "document-end" && 状态 == 浏览框加载状态.静态元素加载完毕) {
                    val 开始阶段脚本 = 用户脚本操作.匹配URL获取脚本(当前URL, "document-start")
                    if (开始阶段脚本.isNotEmpty()) {
                        mlog("同时注入 ${开始阶段脚本.size} 个document-start脚本")
                        注入脚本列表(开始阶段脚本)
                    }
                }
                
                // 注入当前运行时机的脚本
                注入脚本列表(匹配的脚本)
            } catch (e: Exception) {
                mlog("注入用户脚本失败", e)
            }
        }
    }
    
    /**
     * 注入脚本列表
     */
    private suspend fun 注入脚本列表(脚本列表: List<Pair<String, Long>>) {
        for ((脚本路径, _) in 脚本列表) {
            val 脚本内容 = 用户脚本操作.按路径读取脚本内容(脚本路径)
            if (脚本内容 != null) {
                浏览框状态.webview?.evaluateJavascript(脚本内容) {}
            }
        }
    }

    /**
     * 预先匹配适用于当前URL的脚本，并按运行时机分组
     */
    private suspend fun 预匹配脚本(当前URL: String) {
        // 这个方法已不再需要，保留空实现以避免修改太多代码
        // 实际匹配工作已移至用户脚本操作.匹配URL获取脚本方法
    }

    // -- 接口 --
    fun 跳转到阅读模式(){
        应用接口.显示加载中界面(true)
        try {
            浏览框状态.读源码 {
                应用接口.界面模块().加载(阅读页面状态(页面标题 = 标题.value?:"",
                    页面地址 = 地址.value?:"",
                    页面源码缓存标识 = 应用接口.写缓存(it)))
            }
        }finally {
            应用接口.显示加载中界面(false)
        }
    }
    fun 切换翻译状态(){
        浏览框状态.页面配置.启用自动翻译 = !浏览框状态.页面配置.启用自动翻译
        if (浏览框状态.页面配置.启用自动翻译){
            浏览框状态.注入翻译脚本()
        }
        else{
            刷新()
        }
    }
    fun 切换开发者模式(){
        if (浏览框状态.启用开发者模式) 浏览框状态.关闭开发者工具()
        else 浏览框状态.开启开发者工具()
    }
    
    fun 设置电脑模式(启用地址模式 : Boolean){
        浏览框状态.页面配置.启用电脑模式 = 启用地址模式
        刷新()
    }

    /**
     * 离线此页面
     */
    fun 保存页面(){
        浏览框状态.保存页面(应用配置.路径_离线页面.toPath())
    }

    fun 切换页面查找状态(){
        浏览框状态.启用页面内容搜索 = !浏览框状态.启用页面内容搜索
    }

    fun 查看源码(){
        浏览框状态.读源码 {
            应用接口.界面模块().加载(源码标签页状态(应用接口.写缓存(it)))
        }
    }

    fun 查看日记(){
        网页日记窗口状态(日记).显示()
    }

    val 长按菜单列表 = mutableStateListOf<浏览框功能枚举>()
    fun 显示长按菜单(list : List<浏览框功能枚举>){
        长按菜单列表.clear()
        长按菜单列表.addAll(list)
    }
    fun 执行菜单操作(item : 浏览框功能枚举){
        val url = 浏览框状态.webview?.长按选中地址
        val imgUrl = 浏览框状态.webview?.长按选中图片地址
        when(item){
            浏览框功能枚举.新窗口打开 -> {
                应用路由.加载(url?:return,地址.value?:"",true)
            }
            浏览框功能枚举.打开地址 -> {
                应用路由.加载(url?:return,地址.value?:"",false)
            }

            浏览框功能枚举.复制地址 -> {
                ClipboardUtils.copyText(url?:"")
            }
            浏览框功能枚举.查看图片 -> {
                应用路由.加载(imgUrl?:return,地址.value?:"")
            }
            浏览框功能枚举.保存图片 -> {
                // todo 保存图片 应用接口.下载(imgUrl?:return, 地址操作.取文件名(imgUrl,true))
            }
            浏览框功能枚举.标记广告 -> {
                // todo 标记广告
            }
            浏览框功能枚举.识别二维码 -> {
                // todo 识别二维码
            }
            else -> {

            }


        }
    }

    // 这些方法直接委托给浏览框状态实现
    override fun 前进() : Boolean {
        if (浏览框状态.webview?.canGoForward() == true) {
            浏览框状态.webview?.goForward()
            浏览框状态.更新导航状态()
            return true
        }
        return false
    }
    
    override fun 后退() : Boolean {
        if (浏览框状态.webview?.canGoBack() == true) {
            浏览框状态.webview?.goBack()
            浏览框状态.更新导航状态()
            return true
        }
        return false
    }

    override fun 刷新() {
        浏览框状态.刷新()
    }
    
    override fun 界面恢复() {
        浏览框状态.继续()
    }

    override fun 界面暂停() {
        浏览框状态.暂停()
    }
    
    override fun 界面销毁() {
        浏览框状态.销毁()
        super.界面销毁()
    }

    override var 可否添加收藏: Boolean = true
    override fun 添加收藏() {
        val title = 浏览框状态.地址.value
        val url = 浏览框状态.标题.value
        添加书签窗口状态(url,title).显示()
    }

    private fun 添加历史记录(){
        coroutine?.launch {
            val db = GlobalContext.get().get<AppDatabase>()
            val dao = db.历史()

            // 获取当前网页地址和标题
            val 当前地址 = 浏览框状态.地址.value ?: return@launch
            val 当前标题 = 浏览框状态.标题.value ?: 当前地址
            
            // 忽略空地址或特殊地址（比如about:blank）
            if (当前地址.isEmpty() || 当前地址.startsWith("about:") || 当前地址.startsWith("chrome:")) {
                return@launch
            }
            
            // 检查是否存在相同URL的历史记录
            val 已有记录 = dao.根据网址获取历史记录(当前地址)
            
            if (已有记录 != null) {
                // 如果已存在，更新访问时间和访问次数
                已有记录.添加时间 = Clock.System.now().toEpochMilliseconds()
                已有记录.访问次数 += 1
                // 如果标题有变化，更新标题
                if (当前标题.isNotEmpty() && 当前标题 != 已有记录.标题) {
                    已有记录.标题 = 当前标题
                }
                dao.更新历史记录(已有记录)
            } else {
                // 如果不存在，创建新记录
                val 新历史记录 = DBHistory(
                    标题 = 当前标题,
                    网址 = 当前地址,
                    添加时间 = Clock.System.now().toEpochMilliseconds(),
                    访问次数 = 1
                )
                dao.插入历史记录(新历史记录)
            }
        }

        coroutine?.launch {
            浏览框状态.webview?.settings?.userAgentString?.let {
                应用配置.设置本机UA标识(it)
            }

        }
    }
}

